#ifndef _BLOCKBREAKER_COMM_ADMCOMM_H_
#define _BLOCKBREAKER_COMM_ADMCOMM_H_

#include <afxwin.h>
#include <winsock2.h>

#include <map>
#include <string>

#include <Comm/Include/Semaphore.h>
#include <Comm/Include/Channel.h>

typedef enum ExcepcionesAdmComm
{
    BIND_FAILED,
    PASSIVE_OPEN_FAILED    
}
T_ExcepcionesAdmComm;

typedef void (*TCallbackChannelConected)(Channel* canal, char* remote_IP, void* user_data);

typedef struct
{
    SOCKET server_sock;
    unsigned short port_number;
    TCallbackChannelConected func;
    void* user_data;
    Semaphore* sem_exit;
}Tcp_Server_Function_Data;

typedef struct
{
    SOCKET socket;
    SyncQueue* queue;
}Socket_Thread_Data;

enum ADMCOMM_EXCEPTIONS
{
    ADMCOMM_NOT_INITIALIZED,
    ADMCOMM_ALERADY_INITIALIZED
};

class AdmComm
{
    public:
        static AdmComm* getInstance();

        static void Initialize();

        static void Release();

        virtual ~AdmComm(void);

        std::string getLocalIP();

        /// \brief opens a TCP port and waits for connections. 
        /// \details When a connection is available, creates a channel and delivers it 
        /// to the callback function.
        /// \throw BIND_FAILED
        /// \throw PASSIVE_OPEN_FAILED
        void openTCPPassiveConnection(unsigned short port, TCallbackChannelConected f, void* user_data);

        void closeTCPPassiveConnection(unsigned short port);

        /// \brief connects to an ip and port given, returns a channel
        /// \throw ExceptionConnectionFailed
        Channel* TCPconnect(const char* ip_address, unsigned short port);
    
        void closeChannel(Channel* canal);

    private:    
        static AdmComm* instance;

        std::string local_ip;

        static unsigned int TCPServerSocketManager(void* params);

        /// \brief map to contain open ports in a passive way. 
        std::map<unsigned short, SOCKET> server_socks_maps;

        /// \brief map used to stop threads in a passive connection
        std::map<unsigned short, Semaphore*> server_sem_maps;

        AdmComm(void);
};
#endif // _BLOCKBREAKER_COMM_ADMCOMM_H_
